Title Banner

Previous Book Contents Book Index Next

Inside Macintosh: OpenDoc Programmer's Guide / Part 1 - Basics
Chapter 2 - Development Overview


Writing OpenDoc Software

This section briefly summarizes some high-level aspects of the design and implementation of a part editor. It discusses

Developing With SOMobjects and IDL

OpenDoc is implemented as a shared library consisting of a set of classes constructed using SOMobjects for the Mac OS, the implementation of the System Object Model (SOM) for the Mac OS platform. The interfaces to SOM classes must be written in the SOM Interface Definition Language (IDL) and compiled by the SOM compiler, usually separately from the implementations of the classes.

The implementation of OpenDoc objects as SOM objects has several advantages to the use of OpenDoc as a shared library:

Because OpenDoc consists of SOM classes, the class ODPart is naturally a SOM class. If you want your part editor--which is a subclass of ODPart plus any other classes that you define--to also consist of SOM classes, then you must write your interfaces in IDL, separate from your implementations, and you must compile them with the SOM compiler. The result of the compilation is a set of header and stub implementation source files, in one of the procedural or object-oriented programming languages, such as C or C++, supported by the SOM compiler. You complete your development by writing your implementa-
tions into the stub implementation files and compiling them, along with the headers, using a standard compiler for your programming language.

If you write your part-editor interfaces in IDL, you will notice that the IDL syntax is very similar to that of C and C++. It includes essentially the same character set, whitespace rules, comment styles, preprocessing capabilities, identifier-naming rules, and rules for literals. But there are a few differences in source-code appearance to note when declaring or calling methods of SOM-based objects:

Advantages of making all your classes SOM classes include a greater ability to develop portions of your part editor (or set of editors) using different programming languages and compilers, and a lesser need for recompilation of all of your code when you change portions of it. These advantages may be compelling only if your libraries are very large, however, because they must be balanced against the disadvantages of working in both IDL and a separate programming language.

You are not required to make your part-editor classes SOM classes. If you are developing in C++, for example, you can use C++ classes instead. The generally preferred procedure is to create only one SOM class, a subclass of ODPart whose interface contains nothing but your public methods (overrides of the methods of ODPart and its superclasses). That SOM class delegates all of its method calls to a C++ wrapper class, which contains the functionality for the public methods as well as any private fields and methods. Additional classes can be subclasses of your C++ wrapper class.

Advantages of developing in C++ with a single wrapper object include a lesser need to work with interfaces in two languages (IDL and C++), a smaller memory and calling overhead for your objects, and the availability of C++ features (such as templates) that are not supported by SOM.

SOM class ID and editor ID
A SOM class ID is an ISO string whose format is "module::className". A part editor's editor ID is a SOM class ID that uniquely defines the editor; you need to specify your editor ID in your editor's IDL interfaces. For example, the editor ID for AcmeGraph 1.0 might be "Acme::AcmeGraph". Editor IDs are used for binding; see "Information Used for Binding" for more information.
For more information on SOM and using the SOM compiler on the Mac OS platform, see the OpenDoc Cookbook for the Mac OS. For a more detailed description of the Interface Definition Language and instructions on program-
ming with SOM, see, for example, SOMObjects Developer Toolkit Users Guide and SOMObjects Developer Toolkit Programmers Reference Manual from IBM.

OpenDoc Protocols

OpenDoc imposes very few restrictions on how your part editor does its job. The inner workings of your editor's core data engine are of little concern to OpenDoc. The engine should be able to free storage when requested, it should adequately handle cases where the part can be only partially read into memory, and it should handle any multiprocessing issues that arise. Other than that, it simply needs to provide an interface to OpenDoc and use the OpenDoc interface to accomplish OpenDoc-related tasks.

The programming interfaces that your part editor uses (and provides) to perform specific tasks are called protocols. The methods of ODPart, for example, participate in approximately 12 protocols, such as part activation and undo. This section briefly describes the protocols; Table 2-1 lists the methods of ODPart that you have to override to participate in each protocol.

Which Protocols to Participate In

To implement the simplest possible part, you need to participate in only some OpenDoc protocols, and you need to override only some methods of ODPart. As a minimum, your part editor must be able to

Unless it creates extremely simple parts, your part editor must also provide some sort of command interface to the user. It must then be able to

If you wish your parts to be able to contain other parts, your part editor must be able to

Beyond these capabilities, you may want your parts to have additional capabilities, such as drawing themselves asynchronously, providing data-
transfer capability, supporting scripting, or others. You can add those capabilities by overriding other methods of ODPart.

Overriding the Methods of ODPart

Your fundamental programming task in creating an OpenDoc part editor is to subclass the class ODPart and override its methods. The following list summarizes the OpenDoc protocols that your part editor can use and lists the sections in this book that describe each protocol more fully. To create and edit full-featured container parts, your editor must support all of these protocols.

The methods of ODPart involved in each protocol are shown in the table that follows this list.

Table 2-1 lists the methods of ODPart that you must override to have a function-
ing part editor, as well as those that you can optionally override to participate in specific protocols. Note that some protocols, such as layout, imaging, and activation, are required of all part editors, and you must override some or all of the methods associated with them. Other protocols, such as embedding or undo, are not required, and you need not override any of their methods if your parts do not participate. It is, of course, strongly recommended that your parts participate in all protocols that are appropriate to their content model.
Table 2-1 Required and optional ODPart overrides
ProtocolRequired overridesOptional overrides
LayoutAttachSourceFrame
ContainingPartPropertiesUpdated
DisplayFrameAdded
DisplayFrameClosed
DisplayFrameConnected
DisplayFrameRemoved
FacetAdded
FacetRemoved
FrameShapeChanged
GeometryChanged
Open
SequenceChanged
AcquireContainingPartProperties[1]
RevealFrame[1]
ImagingCanvasChanged
Draw
GetPrintResolution
HighlightChanged
PresentationChanged
ViewTypeChanged
AdjustBorderShape[1]
CanvasUpdated[1]
 
ActivationAbortRelinquishFocus
BeginRelinquishFocus
CommitRelinquishFocus
FocusAcquired
FocusLost
 
User eventsAdjustMenus
HandleEvent
 
StorageCloneInto[2]
ClonePartInfo
Externalize[2]
ExternalizeKinds
InitPart
InitPartFromStorage
ReadPartInfo
WritePartInfo
somInit[2]
somUninit[2]
BindingChangeKind 
Memory
Management
ReleaseAll[2]Acquire[2]
Purge[2]
Release[2]
LinkingLinkStatusChangedCreateLink
EditInLinkAttempted[1]
FulfillPromise[3]
LinkUpdated
RevealLink
Embedding CreateEmbeddedFramesIterator[1]
EmbeddedFrameUpdated[1]
RemoveEmbeddedFrame[1]
RequestEmbeddedFrame[1]
RequestFrameShape[1]
UsedShapeChanged[1]
Clipboard FulfillPromise[3]
 
Drag
and drop
 DragEnter
DragLeave
DragWithin
Drop
DropCompleted
FulfillPromise[3]
UndoDisposeActionState
ReadActionState[3]
RedoAction
UndoAction
WriteActionState[3]
Extensions AcquireExtension[2]
HasExtension[2]
ReleaseExtension[2]
Semantic
events
 EmbeddedFrameSpec[1]

Generally, you must override all of the optional methods listed for a given protocol (other than those marked with [3] in Table 2-1) if you are to participate in that protocol. For example, to participate in the extensions protocol, you must override all three methods AcquireExtension, HasExtension, and ReleaseExtension. The embedding protocol has even further requirements; to support embedding, you must not only override all the optional methods listed for that protocol, but you must override several methods associated with other protocols (marked with [1] in Table 2-1).

Development Scenarios

This section contains a high-level discussion of several possible OpenDoc development scenarios. Reading the scenarios may help you to decide what kinds of OpenDoc component software you are most interested in developing. Specifically, it discusses

Writing an Editor for Noncontainer Parts

Writing a part editor that does not support embedding is somewhat simpler than writing an editor that does. Furthermore, if you are starting from scratch (not modifying an existing application), you are free to consider all aspects of the design of your part editor before you implement anything.

  1. Content model. Create a content model that defines the functioning of your part editor and the OpenDoc protocols that it participates in. If your parts are to be scriptable (see step 6), your part's content objects and operations must reflect that content model.

  2. Core engine. Design and implement your core data engine, the set of data structures and behaviors that manifest the basic purpose of your editor.

  3. Storage. Implement persistent storage in these situations:

    • Use the OpenDoc storage system to store your part editor's data. Your part editor must implement code to initialize a part and to write it back to storage.

    • Implement clipboard and drag-and-drop capabilities to transfer informa-
      tion between parts, using the same OpenDoc storage concepts for data transfer as for persistent storage.

    • Implement linking support, using a combination of event-handling code and storage code (similar to support for document storage, clipboard, and drag and drop).

  4. Drawing. Give your part the capability of drawing its content, properly transformed and clipped, in whatever combination of facets and frames the content appears, onscreen or offscreen, and for screen display or for printing.

  5. Event handling. Give your part editor the capability of responding to user events such as mouse clicks and keystrokes. It must also respond properly to activation and deactivation events, and use the OpenDoc menu bar object to manipulate the contents of the menu bar.

  6. Scripting. To support scripting, you must first define the content model of your parts, as noted earlier (step 1). Then you must implement accessor functions to resolve object specifiers (external references to a part's content objects) as well as semantic-event handlers to perform your part's content operations.

  7. Extensions. If you plan to extend the capabilities of your parts to communicate with other parts or process information fast, create and attach an extension interface to your part editor. To obtain existing public extension interface designs or to propose a new public interface, contact CI Labs.

  8. Packaging and shipping. Once your part editor is complete, package one or more stationery documents with your part editor, as templates of your part kind. Provide information (in the form of name-mapping resources on the Mac OS) for the OpenDoc binding process to use, indicating what part kinds are handled, what semantic events are handled, and what extension interfaces are provided.

Once your part editor is complete, you typically create one or more stationery documents, which are empty versions or other kinds of templates for creating parts with your part kind. Stationery documents are commonly blank, but they may have any content you wish, including other embedded parts.

You ship your product as one or more part editors, plus one or more stationery documents, plus accompanying documentation. Users install your part editor into their systems and then, using the stationery, create new documents of your part kind or insert new parts with your part kind into their documents. Rules and conventions for installing part editors and storing documents vary among platforms. For Mac OS platform rules, see Appendix C, "Installing OpenDoc Software and Parts."

Users themselves can create additional, custom stationery documents. Users should be able to exchange documents, including stationery, freely.

Writing an Editor for Container Parts

If your part editor is to support embedding--that is, if its parts are to be container parts--you need to include the following additional steps:

  1. Embedding. You need to add embedding support to your content model and to storage.

    • Your content model needs to include a type of content element that represents an embedded part. If your part editor supports semantic events, embedded parts must be content objects to which you can pass events.

    • Make sure your parts can store embedded parts, both in their documents and during data transfer. This capability is relatively simple to implement; OpenDoc takes care of most of it.

  2. Layout management. You need to add support for layout management during event handling and during frame negotiation.

    • Your part editor must be able to maintain updated information on embedded frames and facets, and notify embedded parts of such changes. It must add facets when embedded frames become visible. It must modify or delete facets when embedded frames move or become no longer visible.

    • Your part editor must include support for layout negotiation, including updating the shapes and transforms associated with each visible embedded frame and facet.

For a summary of the issues to consider in creating a part editor for container parts, see Appendix A, "Embedding Checklist."

Converting a Conventional Application to a Part Editor

Creating a part editor from an existing conventional application involves maintaining its core features but repackaging them for the OpenDoc environment.



  1. Content model and core data engine. You should have your content model and core data engine already in place. You may need to separate your core data engine from other facilities, such as user interface and storage, if it is not already sufficiently separated.

  2. Storage. You must refit all file I/O and clipboard data transfers into OpenDoc terms, as described for part editors in step 3 on page 93.

  3. Event handling. Because your part editor will receive its event information from the document shell, you need to remove your event loop and event-
    handling code.

  4. Scripting. You can add scripting support, as described for part editors in step 6 on page 94.

  5. Extensions. You can extend the capabilities of your parts, as described for part editors in step 7 on page 94.

  6. Packaging and shipping. Package and ship your part editor, as described for part editors in step 8 on page 94.

If your converted application is to be a container part, you need to follow these additional steps:

  1. Embedding. Add embedding support, as described for part editors in step 9 on page 95.

  2. Layout management. Add layout-management code, as described for part editors in step 10 on page 95.

Converting a Conventional Application to a Container Application

If you have an existing application, the simplest way to give it some OpenDoc capabilities may be to convert it into an embedding application (also called a container application), so that it can embed parts. A container application's documents still belong to it but may also contain embedded frames and data from embedded parts.

A container application performs some tasks of a container part, although it never has to act as an embedded part. It also handles some tasks normally performed by the OpenDoc document shell; the document shell is not executing when a container application's document opens. The container application initializes the session object at startup and disposes of it at shutdown. The container application accepts platform-specific user events, converts them to OpenDoc user events, passes them to the dispatcher, and takes care of events that the dispatcher does not handle.

Here are some general considerations to keep in mind if you develop a container application:

  1. Initialization. Because the document shell is not available, your container application needs code to initialize the OpenDoc class library.

  2. Storage. Because all OpenDoc parts in a given document share persistent storage, you must refit all file I/O and clipboard data transfers into OpenDoc terms, as described for part editors in step 3 on page 93. You can use OpenDoc calls to store and retrieve your application's intrinsic data without changing the data format, as long as it is stream-oriented. (The OpenDoc container-application library contains code that helps with reads and writes.) Embedded parts can then also use the regular OpenDoc calls for reading and writing their own data.

    Your container application cannot support OpenDoc drafts of its documents (if it uses the container-application library).

  3. Data transfer. Your container application need do nothing to support clipboard transfer, drag and drop, or linking of content to or from embedded parts. However, for transfers that involve your application's intrinsic content mixed with embedded parts, your application will need to translate between the platform-specific facilities and OpenDoc facilities.

  4. Drawing. Your basic drawing routines need little modification, except that you need to set up your drawing so that it is properly transformed and clipped in your embedded parts' facets and frames. When your container application receives an update event for synchronous drawing, it draws its own content and then passes the event to the OpenDoc dispatcher so that embedded parts can also redraw.

  5. Event handling. Because your application takes the place of the document shell, it needs to pass all user events to the OpenDoc dispatcher first--and handle them only if OpenDoc does not.

    To make this work, you can have a proxy root part, with a frame and facet that correspond to your document window. Events not handled by actual embedded parts are then passed to the proxy part and therefore back to your application for handling. Other parts of OpenDoc can also communicate with those proxies in a normal fashion, and your application can convert those communications into actions that make sense for your application.

    Although OpenDoc supports multiple levels of undo, only one undoable action is preserved when the user switches from an embedded part to your native content. Your container application needs to manage the Undo and Redo menu items itself.

  6. Embedding. You need to add embedding support, as described for part editors in step 9 on page 95.

  7. Layout management. You need to add layout-management code, as described for part editors in step 10 on page 95. Your container application does not necessarily have to support frame negotiation.

  8. Scripting. You can add scripting support, as described for part editors in step 6 on page 94.

To make development of a container application even easier, OpenDoc is distributed with a container-application library (CALib) that contains helpful code. CALib includes support for proxy objects and has other useful features.

Writing Other Types of Component Software

This book primarily describes how to create a part editor. However, you are not limited to that alone. Development of container applications is discussed briefly in the previous section. Using the OpenDoc class libraries, you can also create other kinds of OpenDoc component software:


[1] Required of all parts that support embedding
[2] Defined in a superclass of ODPart
[3] Optional even if you implement this protocol

Previous Book Contents Book Index Next

© Apple Computer, Inc.
16 JUL 1996




Navigation graphic, see text links

Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help